﻿using System;
using System.Collections.Generic;

using Microsoft.Xna.Framework;

using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Collision;

namespace Spacer {
	public class Projectile : Entity {
		public Projectile() {}

		public Projectile( SolarSys world ) : base(world) {
			if( this.GetType() == typeof(Projectile) ) Initialize();
		}

		public override void Initialize() {
			base.Initialize();

            DrawMode = DrawMode.Sprite;
            Sprite = Main.Sprites["Projectile"];

			if( GetType() == typeof(Projectile) ) {
				// No need for a complex object
				PhysicsInitBox( 0.3f, 0.3f, MovementMode.Normal );

				PhysicsObject.Friction = 0.3f;
				PhysicsObject.Mass = 0.1f;
			}

			Health = 10f;
			MaxHealth = 10f;

			Speed = 12f;
			DamageRadius = 2.5f;
			DamageAmount = 45f;
			DamageForce = 15f;

			LifeTime = 3f;
		}

		// ----------

		public override void Update() {
			Vector2 vel = Velocity;
			Angle = (float)Math.Atan2( vel.Y, vel.X );

			base.Update();
		}

		public override bool OnCollision( Entity other, Contact contact ) {
			if( !IsMarkedForRemoval ) {
				if( other is Projectile )
					return false;
				else if( !(other is Planet) )
					Explode();
			}
			return true;
		}

		public void Explode() {
			AABB span = new AABB( WorldCenter, DamageRadius*2f, DamageRadius*2f );
			HashSet<Entity> entities = new HashSet<Entity>();

			// Mark as removed FIRST to prevent infinite loop
			Remove();

			World.Instance.QueryAABB(
				fixture => {
					if( fixture.Body.UserData != null && fixture.Body.UserData is Entity && fixture.Body.UserData != this && !entities.Contains((Entity)fixture.Body.UserData) )
						entities.Add( (Entity)fixture.Body.UserData );
					return true;
				},
				ref span
			);

			Vector2 dir, force;
			float dist, scale;
			foreach(Entity ent in entities) {
				dir = ent.WorldCenter - WorldCenter;	// Get vector between this and other
				dist = dir.Length();			// Get length of vector
				dir /= dist;					// Normalize dir into an actual direction

				if( !ent.Invulnerable && dist <= DamageRadius ) {
					scale = -dist / DamageRadius + 1f;

					ent.TakeDamage( scale*DamageAmount, Owner, this, WorldCenter, dir );

					force = dir * scale * DamageForce;

					if( !(ent is Projectile) )
						ent.PhysicsObject.ApplyForce( force, WorldCenter );
				}
			}
		}

		// ----------

		public float Speed { get; set; }
		public float DamageRadius { get; set; }
		public float DamageAmount { get; set; }
		public float DamageForce { get; set; }
	}
}
